//! UDP 套接字服务器和客户端的示例。
//!
//! 此示例将启动一个在 `127.0.0.1:8080` 上监听的 UDP 服务器。
//!
//! 您可以使用 `netcat` 连接到此服务器：
//!
//! ```
//! $ netcat -u 127.0.0.1:8080
//! Hello world
//! ```
//!
//! 服务器将响应 `Hello world`。

use std::net::UdpSocket;

/// # `std::net::UdpSocket` 示例
///
/// ## 功能
///
/// `UdpSocket` 用于通过 UDP 协议发送和接收数据。
///
/// ## `bind`
///
/// `bind` 方法用于将套接字绑定到本地地址。
///
/// ## `send_to`
///
/// `send_to` 方法用于将数据发送到指定的远程地址。
///
/// ## `recv_from`
///
/// `recv_from` 方法用于从套接字接收数据，并返回接收到的字节数和数据的来源地址。
#[test]
pub fn udp_socket_example() {
    // 绑定到本地地址和端口
    let socket = UdpSocket::bind("127.0.0.1:8080").expect("无法绑定套接字");

    // 创建一个缓冲区来存储接收到的数据
    let mut buf = [0; 1024];

    // 循环接收和发送数据
    loop {
        // 从套接字接收数据
        let (amt, src) = socket.recv_from(&mut buf).expect("无法接收数据");

        // 将接收到的数据转换为字符串并打印
        let received = std::str::from_utf8(&buf[..amt]).unwrap();
        println!("从 {} 接收到 {} 字节: {}", src, amt, received);

        // 将接收到的数据发送回来源地址
        socket.send_to(&buf[..amt], src).expect("无法发送数据");
    }
}

// ================== 补充 UDP 客户端发送案例 ==================
/// # UDP 客户端发送数据示例
/// 此函数演示如何作为客户端向 UDP 服务器发送数据并接收响应。
#[test]
pub fn udp_client_example() {
    // 绑定到任意本地端口（由操作系统分配）
    let socket = UdpSocket::bind("127.0.0.1:0").expect("无法绑定客户端套接字");
    // 目标服务器地址
    let server_addr = "127.0.0.1:8080";
    // 要发送的数据
    let msg = b"Hello UDP Server!";
    // 发送数据到服务器
    socket.send_to(msg, server_addr).expect("发送失败");
    // 接收服务器响应
    let mut buf = [0u8; 1024];
    let (amt, src) = socket.recv_from(&mut buf).expect("接收失败");
    let resp = std::str::from_utf8(&buf[..amt]).unwrap();
    println!("收到来自 {} 的响应: {}", src, resp);
}

// ================== 补充：优雅退出循环的服务器示例 ==================
/// # UDP 服务器（可优雅退出）
/// 通过接收到 "exit" 字符串时退出循环。
#[test]
pub fn udp_server_with_exit() {
    let socket = UdpSocket::bind("127.0.0.1:8081").expect("无法绑定套接字");
    let mut buf = [0u8; 1024];
    loop {
        let (amt, src) = socket.recv_from(&mut buf).expect("无法接收数据");
        let received = std::str::from_utf8(&buf[..amt]).unwrap();
        println!("从 {} 接收到: {}", src, received);
        // 关键注释：收到 "exit" 时退出循环
        if received.trim() == "exit" {
            println!("收到退出指令，服务器即将关闭。");
            break;
        }
        socket.send_to(&buf[..amt], src).expect("无法发送数据");
    }
}

// ================== 补充：常见错误处理与超时设置 ==================
/// # UDP 套接字超时设置与错误处理
#[test]
pub fn udp_socket_timeout_example() {
    let socket = UdpSocket::bind("127.0.0.1:8082").expect("无法绑定套接字");
    // 设置接收超时时间为 2 秒
    socket
        .set_read_timeout(Some(std::time::Duration::from_secs(2)))
        .expect("设置超时失败");
    let mut buf = [0u8; 1024];
    match socket.recv_from(&mut buf) {
        Ok((amt, src)) => {
            let received = std::str::from_utf8(&buf[..amt]).unwrap();
            println!("收到来自 {} 的数据: {}", src, received);
        }
        Err(e) => {
            // 关键注释：超时或其他错误处理
            println!("接收数据时发生错误: {}", e);
        }
    }
}
